package com.inspur.dgov.common.secure.utils;

import cn.hutool.core.convert.Convert;
import com.inspur.dgov.common.secure.constant.SecureConstant;
import com.inspur.dgov.common.secure.constant.TokenConstant;
import com.inspur.dgov.common.secure.dto.SecureUser;
import com.inspur.dgov.common.secure.exception.SecureException;
import com.inspur.dgov.common.tool.utils.Charsets;
import com.inspur.dgov.common.tool.utils.SpringContextHolder;
import com.inspur.dgov.common.tool.utils.StringPool;
import com.inspur.dgov.common.tool.utils.WebUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.SneakyThrows;
import org.redisson.api.RedissonClient;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.*;

/**
 * Secure工具类
 *
 * @Author chiyupei
 * @Date 19/7/1 上午9:58
 * @Version 1.0
 * @Description Secure工具类
 */
public class SecureUtil {

    private static final String USER_REQUEST_ATTR = "_USER_REQUEST_ATTR_";

    private static String BASE64_SECURITY = Base64.getEncoder().encodeToString(TokenConstant.SIGN_KEY.getBytes(Charsets.UTF_8));

    private static RedissonClient redissonClient;

    static {
        redissonClient = SpringContextHolder.getBean(RedissonClient.class);
    }

    /**
     * 获取用户信息
     *
     * @return DgovUser
     */
    public static SecureUser getUser() {
        HttpServletRequest request = WebUtil.getRequest();
        if (request == null) {
            return null;
        }
        // 优先从 request 中获取
        Object user = request.getAttribute(USER_REQUEST_ATTR);
        if (user == null) {
            //先从Redis中获取
            user = getUserRedis(request);
            if (user == null) {
                user = getUser(request);
            }
            if (user != null) {
                // 设置到 request 中
                request.setAttribute(USER_REQUEST_ATTR, user);
            }
        }
        return (SecureUser) user;
    }

    /**
     * 获取用户信息
     *
     * @param request request
     * @return DgovUser
     */
    public static SecureUser getUser(HttpServletRequest request) {
        Claims claims = getClaims(request);
        if (claims == null) {
            return null;
        }

        String clientId = Convert.toStr(claims.get(TokenConstant.CLIENT_ID));
        String userId = Convert.toStr(claims.get(TokenConstant.USER_ID));
        String roleId = Convert.toStr(claims.get(TokenConstant.ROLE_ID));
        String userName = Convert.toStr(claims.get(TokenConstant.USER_NAME));
        String userType = Convert.toStr(claims.get(TokenConstant.USER_TYPE));
        String tenantId = Convert.toStr(claims.get(TokenConstant.TENANT_ID));
        SecureUser user = new SecureUser();
        user.setClientId(clientId);
        user.setUserId(userId);
        user.setUserName(userName);
        user.setRoleId(roleId);
        user.setUserType(userType);
        user.setTenantId(tenantId);
        return user;
    }

    /**
     * 获取用户id
     *
     * @return userId
     */
    public static String getUserId() {
        SecureUser user = getUser();
        return (null == user) ? "-1" : user.getUserId();
    }

    /**
     * 获取租户ID
     *
     * @return tenantId
     */
    public static String getTenantId() {
        SecureUser user = getUser();
        return (null == user) ? "" : user.getTenantId();
    }

    /**
     * 获取Claims
     *
     * @param request request
     * @return Claims
     */
    public static Claims getClaims(HttpServletRequest request) {
        String auth = request.getHeader(TokenConstant.HEADER);
        if ((auth != null) && (auth.length() > TokenConstant.AUTH_LENGTH)) {
            String headStr = auth.substring(0, 6).toLowerCase();
            if (headStr.compareTo(TokenConstant.BEARER) == 0) {
                auth = auth.substring(7);
                return SecureUtil.parseJWT(auth);
            }
        }
        return null;
    }

    /**
     * 解析jsonWebToken
     *
     * @param jsonWebToken jsonWebToken
     * @return Claims
     */
    public static Claims parseJWT(String jsonWebToken) {
        try {
            return Jwts.parser()
                    .setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY))
                    .parseClaimsJws(jsonWebToken).getBody();
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * 创建令牌
     *
     * @param user     user
     * @param audience audience
     * @param issuer   issuer
     * @param isExpire isExpire
     * @return jwt
     */
    public static String createJWT(Map<String, Object> user, String audience, String issuer, boolean isExpire) {

        String[] tokens = extractAndDecodeHeader();
        String clientId = tokens[0];

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //生成签名密钥
        byte[] apiKeySecretBytes = Base64.getDecoder().decode(BASE64_SECURITY);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        //添加构成JWT的类
        JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken")
                .setIssuer(issuer)
                .setAudience(audience)
                .signWith(signatureAlgorithm, signingKey);

        //设置JWT参数
        user.forEach(builder::claim);
        builder.claim("client_id", clientId);

        //添加Token过期时间
        if (isExpire) {
            long expMillis = nowMillis + getExpire();
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }

        //生成JWT
        return builder.compact();
    }

    /**
     * 获取过期时间(次日凌晨3点)
     *
     * @return expire
     */
    public static long getExpire() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        cal.set(Calendar.HOUR_OF_DAY, 3);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTimeInMillis() - System.currentTimeMillis();
    }

    /**
     * 客户端信息解码
     */
    @SneakyThrows
    public static String[] extractAndDecodeHeader() {
        // 获取请求头客户端信息
        String header = Objects.requireNonNull(WebUtil.getRequest()).getHeader(SecureConstant.BASIC_HEADER_KEY);
        if (header == null || !header.startsWith(SecureConstant.BASIC_HEADER_PREFIX)) {
            throw new SecureException("No client information in request header");
        }
        byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8);

        byte[] decoded;
        try {
            decoded = Base64.getDecoder().decode(base64Token);
        } catch (IllegalArgumentException var7) {
            throw new RuntimeException("Failed to decode basic authentication token");
        }

        String token = new String(decoded, Charsets.UTF_8);
        int index = token.indexOf(StringPool.COLON);
        if (index == -1) {
            throw new RuntimeException("Invalid basic authentication token");
        } else {
            return new String[]{token.substring(0, index), token.substring(index + 1)};
        }
    }

    public static Object getUserRedis(HttpServletRequest request) {
        if (null != redissonClient) {
            String auth = request.getHeader(TokenConstant.HEADER);
            if ((auth != null) && (auth.length() > TokenConstant.AUTH_LENGTH)) {
                String headStr = auth.substring(0, 6).toLowerCase();
                if (headStr.compareTo(TokenConstant.BEARER) == 0) {
                    auth = auth.substring(7);
                    return redissonClient.getBucket(auth).get();
                }
            }
        }
        return null;
    }
}
